package com.ruoyi.water.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.water.domain.PressureAuthorization;
import com.ruoyi.water.domain.PressureDevice;
import com.ruoyi.water.domain.PressureSensor;
import com.ruoyi.water.mapper.PressureAuthorizationMapper;
import com.ruoyi.water.service.IPressureDeviceService;
import com.ruoyi.water.service.IPressureSensorService;
import com.ruoyi.water.service.IPressureTokenService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import jakarta.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class PressureTokenServiceImpl implements IPressureTokenService {

    // ====================== 常量配置（严格对应文档+数据库） ======================
    private static final String GET_DEVICE_SENSOR_URL = "https://app.dtuip.com/api/device/getDeviceSensorDatas";
    private static final String CONFIG_CLIENT_ID = "clientId";
    private static final String CONFIG_CLIENT_SECRET = "clientSecret";
    private static final String CONFIG_USER_ID = "userId";
    private static final String CONFIG_USERNAME = "username";
    private static final String CONFIG_PASSWORD = "password";
    private static final String CONFIG_AUTHORIZATION = "Authorization";
    private static final Integer PAGE_SIZE = 100;
    private static final String REDIS_ACCESS_TOKEN_KEY = "pressure:api:access_token";
    private static final String REDIS_REFRESH_TOKEN_KEY = "pressure:api:refresh_token";
    private static final String GET_TOKEN_URL = "https://app.dtuip.com/oauth/token?grant_type=password&username=%s&password=%s";
    private static final String REFRESH_TOKEN_URL = "https://app.dtuip.com/oauth/token?grant_type=refresh_token&refresh_token=%s&client_id=%s&client_secret=%s";


    // ====================== 依赖注入 ======================
    @Resource
    private PressureAuthorizationMapper authorizationMapper;
    @Resource
    private IPressureDeviceService deviceService;
    @Resource
    private IPressureSensorService sensorService;
    @Resource
    private RedisTemplate<String, String> stringRedisTemplate;
    @Resource
    private RestTemplate restTemplate;
    private final ObjectMapper objectMapper = new ObjectMapper();


    // ====================== 内部DTO类（无修改，保持与接口对齐） ======================
    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    private static class PressureTokenVO {
        @JsonProperty("access_token")
        private String accessToken;
        @JsonProperty("refresh_token")
        private String refreshToken;
        @JsonProperty("expires_in")
        private Long expiresIn;
        @JsonProperty("clientId")
        private String clientId;
        @JsonProperty("clientSecret")
        private String clientSecret;
    }

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    private static class DeviceSensorPageRequest {
        @JsonProperty("userId")
        private String userId;
        @JsonProperty("currPage")
        private Integer currPage;
        @JsonProperty("pageSize")
        private Integer pageSize;
    }

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    private static class DeviceSensorPageResponse {
        @JsonProperty("flag")
        private String flag;
        @JsonProperty("msg")
        private String msg;
        @JsonProperty("currPage")
        private Integer currPage;
        @JsonProperty("pages")
        private Integer pages;
        @JsonProperty("pageSize")
        private Integer pageSize;
        @JsonProperty("rowCount")
        private Integer rowCount;
        @JsonProperty("dataList")
        private List<PressureDeviceVO> dataList;
    }

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    private static class PressureDeviceVO {
        @JsonProperty("id")
        private String id;
        @JsonProperty("deviceName")
        private String deviceName;
        @JsonProperty("isLine")
        private Integer isLine;
        @JsonProperty("isAlarms")
        private String isAlarmsStr;
        @JsonProperty("createDate")
        private String createDateStr;
        @JsonProperty("linktype")
        private String linktype;
        @JsonProperty("sensorsList")
        private List<PressureSensorVO> sensorsList;
    }

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    private static class PressureSensorVO {
        @JsonProperty("id")
        private String id;
        @JsonProperty("sensorName")
        private String sensorName;
        @JsonProperty("sensorTypeId")
        private Integer sensorTypeId;
        @JsonProperty("value")
        private String valueStr; // 接口返回仍为字符串，无需修改
        @JsonProperty("isAlarms")
        private String isAlarmsStr;
        @JsonProperty("updateDate")
        private String updateDateStr;
    }


    // ====================== Token核心方法（无修改） ======================
    @Override
    public String getAvailableAccessToken() {
        String accessToken = stringRedisTemplate.opsForValue().get(REDIS_ACCESS_TOKEN_KEY);
        if (StringUtils.hasText(accessToken)) {
            log.info("从Redis获取到有效accessToken");
            return accessToken;
        }

        String refreshToken = stringRedisTemplate.opsForValue().get(REDIS_REFRESH_TOKEN_KEY);
        if (StringUtils.hasText(refreshToken)) {
            try {
                PressureTokenVO refreshedToken = refreshAccessToken(refreshToken);
                log.info("用refreshToken刷新accessToken成功");
                return refreshedToken.getAccessToken();
            } catch (Exception e) {
                log.warn("refreshToken刷新失败，将重新初始获取token：", e);
            }
        }

        PressureTokenVO newToken = fetchAccessToken();
        log.info("初始获取accessToken成功");
        return newToken.getAccessToken();
    }

    private PressureTokenVO fetchAccessToken() {
        try {
            String username = getConfigByKey(CONFIG_USERNAME);
            String password = getConfigByKey(CONFIG_PASSWORD);
            String basicAuth = getBasicAuthHeader();

            String requestUrl = String.format(GET_TOKEN_URL, username, password);
            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", basicAuth);
            headers.setContentType(MediaType.TEXT_PLAIN);
            HttpEntity<Void> requestEntity = new HttpEntity<>(headers);

            ResponseEntity<String> response = restTemplate.exchange(
                    requestUrl, HttpMethod.POST, requestEntity, String.class
            );
            if (!response.getStatusCode().is2xxSuccessful() || !StringUtils.hasText(response.getBody())) {
                throw new RuntimeException("获取accessToken响应失败，状态码：" + response.getStatusCode());
            }

            PressureTokenVO tokenVO = objectMapper.readValue(response.getBody(), PressureTokenVO.class);
            storeTokenToRedis(tokenVO);
            return tokenVO;
        } catch (Exception e) {
            throw new RuntimeException("初始获取accessToken失败：", e);
        }
    }

    private PressureTokenVO refreshAccessToken(String refreshToken) {
        try {
            String clientId = getConfigByKey(CONFIG_CLIENT_ID);
            String clientSecret = getConfigByKey(CONFIG_CLIENT_SECRET);

            String requestUrl = String.format(REFRESH_TOKEN_URL, refreshToken, clientId, clientSecret);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.TEXT_PLAIN);
            HttpEntity<Void> requestEntity = new HttpEntity<>(headers);

            ResponseEntity<String> response = restTemplate.exchange(
                    requestUrl, HttpMethod.POST, requestEntity, String.class
            );
            if (!response.getStatusCode().is2xxSuccessful() || !StringUtils.hasText(response.getBody())) {
                throw new RuntimeException("刷新accessToken响应失败，状态码：" + response.getStatusCode());
            }

            PressureTokenVO tokenVO = objectMapper.readValue(response.getBody(), PressureTokenVO.class);
            storeTokenToRedis(tokenVO);
            return tokenVO;
        } catch (Exception e) {
            throw new RuntimeException("刷新accessToken失败：", e);
        }
    }

    private String getBasicAuthHeader() {
        String dbAuth = getConfigByKey(CONFIG_AUTHORIZATION);
        if (StringUtils.hasText(dbAuth) && dbAuth.startsWith("Basic ")) {
            return dbAuth;
        }

        String clientId = getConfigByKey(CONFIG_CLIENT_ID);
        String clientSecret = getConfigByKey(CONFIG_CLIENT_SECRET);
        String authStr = clientId + ":" + clientSecret;
        String base64Str = Base64.getEncoder().encodeToString(authStr.getBytes(StandardCharsets.UTF_8));
        return "Basic " + base64Str;
    }

    private String getConfigByKey(String key) {
        PressureAuthorization config = authorizationMapper.selectOne(
                new LambdaQueryWrapper<PressureAuthorization>()
                        .eq(PressureAuthorization::getKey, key)
        );
        if (config == null || !StringUtils.hasText(config.getValue())) {
            throw new RuntimeException("数据库配置缺失：key=" + key);
        }
        return config.getValue();
    }

    private void storeTokenToRedis(PressureTokenVO tokenVO) {
        long accessTokenExpire = tokenVO.getExpiresIn() - 60;
        stringRedisTemplate.opsForValue().set(
                REDIS_ACCESS_TOKEN_KEY, tokenVO.getAccessToken(),
                accessTokenExpire, TimeUnit.SECONDS
        );

        stringRedisTemplate.opsForValue().set(
                REDIS_REFRESH_TOKEN_KEY, tokenVO.getRefreshToken(),
                7, TimeUnit.DAYS
        );
    }


    // ====================== 设备传感器同步方法（核心修改：适配double类型） ======================
    @Override
    public void syncAllDeviceAndSensorData() {
        try {
            String userId = getConfigByKey(CONFIG_USER_ID);
            String clientId = getConfigByKey(CONFIG_CLIENT_ID);
            String accessToken = getAvailableAccessToken();
            if (!StringUtils.hasText(userId)) {
                throw new RuntimeException("同步失败：缺失userId配置（key=userId）");
            }
            if (!StringUtils.hasText(clientId)) {
                throw new RuntimeException("同步失败：缺失clientId配置（key=clientId）");
            }
            log.info("开始同步设备传感器数据：userId={}，clientId={}", userId, clientId);

            List<PressureDeviceVO> allDeviceVOList = new ArrayList<>();
            Integer currPage = 1;
            while (true) {
                DeviceSensorPageResponse pageResponse = getDeviceSensorByPage(
                        userId, clientId, accessToken, currPage, PAGE_SIZE
                );

                List<PressureDeviceVO> currentPageDevices = pageResponse.getDataList();
                if (CollectionUtils.isNotEmpty(currentPageDevices)) {
                    allDeviceVOList.addAll(currentPageDevices);
                    log.info("同步进度：第{}页，当前页设备数={}，累计设备数={}",
                            currPage, currentPageDevices.size(), allDeviceVOList.size());
                }

                Integer totalPages = pageResponse.getPages();
                if (currPage >= totalPages) {
                    log.info("同步完成：总页数={}，总设备数={}", totalPages, allDeviceVOList.size());
                    break;
                }
                currPage++;
            }

            // 数据转换：VO→Entity（sensor的value已改为double）
            List<PressureDevice> deviceEntityList = parseDeviceVOToEntity(allDeviceVOList);
            List<PressureSensor> sensorEntityList = parseSensorVOToEntity(allDeviceVOList);

            if (CollectionUtils.isNotEmpty(deviceEntityList)) {
                boolean deviceResult = deviceService.saveOrUpdateBatch(deviceEntityList);
                log.info("设备同步结果：总数{}，{}", deviceEntityList.size(), deviceResult ? "成功" : "部分失败");
            } else {
                log.warn("无设备数据需同步");
            }
            if (CollectionUtils.isNotEmpty(sensorEntityList)) {
                boolean sensorResult = sensorService.saveOrUpdateBatch(sensorEntityList);
                log.info("传感器同步结果：总数{}，{}", sensorEntityList.size(), sensorResult ? "成功" : "部分失败");
            } else {
                log.warn("无传感器数据需同步");
            }

        } catch (Exception e) {
            log.error("设备传感器同步异常：", e);
            throw new RuntimeException("同步失败：" + e.getMessage());
        }
    }

    // 分页获取设备传感器数据（无修改）
    private DeviceSensorPageResponse getDeviceSensorByPage(String userId, String clientId, String accessToken, Integer currPage, Integer pageSize) {
        try {
            DeviceSensorPageRequest requestDTO = new DeviceSensorPageRequest();
            requestDTO.setUserId(userId);
            requestDTO.setCurrPage(currPage);
            requestDTO.setPageSize(pageSize);
            log.debug("设备传感器请求参数（第{}页）：{}", currPage, objectMapper.writeValueAsString(requestDTO));

            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", "Bearer " + accessToken);
            headers.set("tlinkAppId", clientId);
            headers.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<DeviceSensorPageRequest> requestEntity = new HttpEntity<>(requestDTO, headers);

            // 注意：此处原代码HTTP方法标注为"修正为GET"，但实际仍写为POST，需根据接口文档确认（若文档是GET则改为HttpMethod.GET）
            ResponseEntity<String> response = restTemplate.exchange(
                    GET_DEVICE_SENSOR_URL,
                    HttpMethod.POST,
                    requestEntity,
                    String.class
            );

            if (!response.getStatusCode().is2xxSuccessful()) {
                throw new RuntimeException(String.format(
                        "设备传感器接口HTTP失败（第%s页）：状态码=%s，响应体=%s",
                        currPage, response.getStatusCode(), response.getBody()
                ));
            }
            String responseBody = response.getBody();
            if (!StringUtils.hasText(responseBody)) {
                throw new RuntimeException("设备传感器接口返回空响应（第" + currPage + "页）");
            }
            log.debug("设备传感器接口响应（第{}页）：{}", currPage, responseBody);

            DeviceSensorPageResponse pageResponse = objectMapper.readValue(responseBody, DeviceSensorPageResponse.class);
            if (!"00".equals(pageResponse.getFlag())) {
                throw new RuntimeException(String.format(
                        "设备传感器接口业务失败（第%s页）：flag=%s，msg=%s",
                        currPage, pageResponse.getFlag(), pageResponse.getMsg()
                ));
            }

            return pageResponse;

        } catch (RestClientException e) {
            throw new RuntimeException("设备传感器接口HTTP请求异常（第" + currPage + "页）：", e);
        } catch (Exception e) {
            throw new RuntimeException("设备传感器接口处理异常（第" + currPage + "页）：", e);
        }
    }

    // 设备VO转Entity（无修改）
    private List<PressureDevice> parseDeviceVOToEntity(List<PressureDeviceVO> deviceVOList) {
        List<PressureDevice> entityList = new ArrayList<>();
        for (PressureDeviceVO vo : deviceVOList) {
            if (Objects.isNull(vo.getId()) || !StringUtils.hasText(vo.getId())) {
                log.warn("跳过设备ID为空的数据：deviceName={}", vo.getDeviceName());
                continue;
            }

            PressureDevice entity = new PressureDevice();
            entity.setDeviceId(Integer.valueOf(vo.getId()));
            entity.setDeviceName(StringUtils.hasText(vo.getDeviceName()) ? vo.getDeviceName() : "未知设备");
            String alarmsFlag = StringUtils.hasText(vo.getIsAlarmsStr()) ? vo.getIsAlarmsStr() : "";
            entity.setIsAlarms("1".equals(alarmsFlag));
            entity.setIsLine(Objects.nonNull(vo.getIsLine()) && vo.getIsLine() == 1);

            entityList.add(entity);
        }
        return entityList;
    }

    /**
     * 传感器VO转Entity（核心修改：value字段从Integer改为Double）
     * 数据库表：t_pressure_sensor（sensorId int, deviceId int, sensorName varchar, value <span style="color:red">double</span>, isAlarms bit=b'0', sensorTypeId int=1, updateDate timestamp）
     */
    private List<PressureSensor> parseSensorVOToEntity(List<PressureDeviceVO> deviceVOList) {
        List<PressureSensor> entityList = new ArrayList<>();
        for (PressureDeviceVO deviceVo : deviceVOList) {
            if (Objects.isNull(deviceVo.getId()) || !StringUtils.hasText(deviceVo.getId())) {
                log.warn("设备ID为空，跳过其传感器数据：deviceName={}", deviceVo.getDeviceName());
                continue;
            }

            List<PressureSensorVO> sensorVoList = deviceVo.getSensorsList();
            if (CollectionUtils.isEmpty(sensorVoList)) {
                log.info("设备{}（ID：{}）无传感器数据，跳过", deviceVo.getDeviceName(), deviceVo.getId());
                continue;
            }

            for (PressureSensorVO sensorVo : sensorVoList) {
                if (Objects.isNull(sensorVo.getId()) || !StringUtils.hasText(sensorVo.getId())) {
                    log.warn("跳过传感器ID为空的数据（设备ID：{}）", deviceVo.getId());
                    continue;
                }

                PressureSensor entity = new PressureSensor();
                entity.setSensorId(Integer.valueOf(sensorVo.getId()));
                entity.setDeviceId(Integer.valueOf(deviceVo.getId()));
                entity.setSensorName(StringUtils.hasText(sensorVo.getSensorName()) ? sensorVo.getSensorName() : "未知传感器");
                // 核心修改1：value字段类型改为Double，调用新的转换方法
                entity.setValue(parseSensorValueToDouble(sensorVo.getValueStr()));
                String alarmsFlag = StringUtils.hasText(sensorVo.getIsAlarmsStr()) ? sensorVo.getIsAlarmsStr() : "";
                entity.setIsAlarms("1".equals(alarmsFlag));
                entity.setSensorTypeId(Objects.nonNull(sensorVo.getSensorTypeId()) ? sensorVo.getSensorTypeId() : 1);
                entity.setUpdateDate(parseUpdateDate(sensorVo.getUpdateDateStr()));

                entityList.add(entity);
            }
        }
        return entityList;
    }

    /**
     * 传感器数值转换（核心修改：从"字符串转int"改为"字符串转double"，保留原始精度）
     */
    private Double parseSensorValueToDouble(String valueStr) {
        if (!StringUtils.hasText(valueStr)) {
            return 0.0; // 空值默认0.0（适配double类型）
        }
        try {
            // 直接转double，不做四舍五入，保留接口返回的原始精度（如10.23、5.0000等）
            return Double.parseDouble(valueStr);
        } catch (NumberFormatException e) {
            log.warn("传感器数值转换失败，设为默认值0.0：value={}", valueStr);
            return 0.0;
        }
    }

    // 时间转换（无修改）
    private Timestamp parseUpdateDate(String dateStr) {
        if (!StringUtils.hasText(dateStr)) {
            return null;
        }
        try {
            return Timestamp.valueOf(dateStr);
        } catch (IllegalArgumentException e) {
            log.warn("时间格式转换失败，设为null：date={}", dateStr);
            return null;
        }
    }
}